// $codepro.audit.disable lossOfPrecisionInCast , numericLiterals
/**
 * This file was created by the SockFullOfOranges group for use in SpaceTrader
 */

import javax.swing.JPanel;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

/**
 * This class is responsible for making the GUI of the Space Map panel, which
 * displays the visual representations of planets, their information, and the
 * associated visual effects.
 * 
 * @author Nick Popescu
 * @version 1.0
 */

public class SpaceMapScreen extends JPanel implements MouseMotionListener,
	MouseListener {

    /**
     * Space.
     */

    private final Space space;

    /**
     * Size of the Planet names text.
     */
    private static final int TEXTSIZE = 18;

    /**
     * The default size of the planet
     */

    private static final int DEFAULTPLANETSIZE = 2; // The size that the planets
						    // will be drawn by default.

    /**
     * The size of the planet that the player is currently on
     */

    private static final int PLAYERPLANETSIZE = 5; // The size of the planet
						   // that hte player is
						   // currently on.

    /**
     * The maximum size that the planet can be drawn
     */

    private static final int MAXPLANETSIZE = 7; // The maximum size that the
						// planet can be drawn.

    /**
     * Distance, in pixels, from a planet, to allow its size to scale.
     */

    private static final double MOUSERANGE = 25; // Distance, in pixels, from a
						 // planet,
						 // to allow its size to scale.

    /**
     * Distance from center of planet to make it clickable.
     */

    private static final int ACTIVATIONDISTANCE = 5; // Distance from center of
						     // planet
						     // to make it clickable.

    /**
     * Number of Columns on the visual background grid on the map.
     */

    private static final int GRIDNUMCOLS = 10; // Number of Columns on the
					       // visual
					       // background grid on the map.

    /**
     * Number of Rows on the visual background grid on the map.
     */

    private static final int GRIDNUMROWS = 5; // Number of Rows on the visual
					      // background grid on the map.

    /**
     * The color of the regular planets.
     */

    private final Color regPlanetColor = Color.white;// The color of the regular
						     // planets.

    /**
     * Color of planet selected
     */

    private final Color selectedPlanetColor = new Color((float) .9, (float) .9, // $codepro.audit.disable
	    (float) .9, (float) .1);// $codepro.audit.disable // The color of
				    // the selected planet.

    /**
     * Color of the box
     */

    private final Color boxColor = new Color((float) .1, (float) .1, // $codepro.audit.disable
	    (float) .1, (float) .5);// $codepro.audit.disable

    /**
     * Color of info box name
     */

    private final Color infoBoxNameColor = Color.green;

    /**
     * Color of info box text
     */

    private final Color infoBoxTextColor = Color.orange;

    /**
     * Color of the planet the player is on
     */

    private final Color playerPlanetColor = Color.green;

    /**
     * Color of radius
     */

    private final Color radiusColor = Color.white;

    /**
     * Create the space panel. Also sets up Mouse listeners.
     * 
     * @param space
     *            This is the Space grid that will be used to draw the map.
     */

    public SpaceMapScreen(Space space) { // $codepro.audit.disable
					 // com.instantiations.assist.eclipse.analysis.audit.rule.effectivejava.constructorsOnlyInvokeFinalMethods
	setOpaque(false);
	this.space = space;
	addMouseMotionListener(this);
	addMouseListener(this);

    }

    /**
     * This is what draws everything on the Space panel. It draws the planets,
     * the info boxes, etc.
     * 
     * @param g
     *            The required Graphics component that everything is drawn on.
     */
    @Override
    public void paintComponent(Graphics g) {
	super.paintComponent(g); // paints all the parent stuff.

	final Graphics2D g2d = (Graphics2D) g; // makes a new Graphics2D, which
					       // provides drawing tools.

	final Planet[] planetArray = space.getPlanets(); // sets up a reference
							 // to the
							 // planet array.

	int rectX = 0, rectY = 0, rectWidth = 0, rectHeight = 0; // These are
								 // attributes
								 // of the
								 // floaty
								 // info Box

	Planet selectedPlanet = null; // This is the planet that is currently
				      // being hovered over.

	/*
	 * Sets the color of the background grid. It's gray, with a transparency
	 * of .1
	 */
	g2d.setColor(selectedPlanetColor);

	/*
	 * Draws vertical and horizontal lines for hte background grid.
	 */
	for (int i = 0; i < GRIDNUMCOLS; i++) {
	    g2d.drawLine(i * (this.getWidth() / GRIDNUMCOLS), 0,
		    i * (this.getWidth() / GRIDNUMCOLS), this.getHeight());
	}

	for (int i = 0; i < GRIDNUMROWS; i++) {
	    g2d.drawLine(0, i * (this.getHeight() / GRIDNUMROWS),
		    this.getWidth(), i * (this.getHeight() / GRIDNUMROWS));
	}

	g2d.setColor(regPlanetColor); // Sets the color of the planets.

	/*
	 * This is where the planets are drawn.
	 */
	for (int i = 0; i < planetArray.length; i++) {
	    Planet currPlanet = planetArray[i]; // Currently-drawn planet.

	    /*
	     * Sets this planet's Visual Location. The Visual Location is
	     * different from the Standard Location in that it accounts for the
	     * size of the Panel, to distribute the planets all the way across
	     * the Panel.
	     */
	    currPlanet.setVisualLocation(new Point((int) (((currPlanet
		    .getLocation().getX()) / space // $codepro.audit.disable
						   // lossOfPrecisionInCast
		    .getGridSize().getWidth()) * this.getWidth()),
		    (int) (((currPlanet.getLocation().getY()) / space // $codepro.audit.disable
								      // lossOfPrecisionInCast
			    .getGridSize().getHeight()) * this.getHeight())));

	    /*
	     * If this planet is currently being hovered over by the mouse...
	     */
	    if (currPlanet.isClickable()) {

		selectedPlanet = currPlanet; // Keeps a reference to this
					     // planet.
		g2d.setColor(Color.orange); // Makes the Selected Planet orange.

		/*
		 * Draws the Selected planet. Fills it with Orange. Also,
		 * offsets it by its radius in order to make its Center as the
		 * visual XY coordinate of the planet. In other words, the XY
		 * coordinates of the planet will be treated as the center of
		 * the planet, not its top left corner.
		 */
		g2d.fillOval(
			(int) (currPlanet.getVisualLocation().getX() - currPlanet
				.getDrawSize() / 2.0), (int) (currPlanet
				.getVisualLocation().getY() - currPlanet
				.getDrawSize() / 2.0),
			currPlanet.getDrawSize(), currPlanet.getDrawSize());

		/*
		 * If this planet is NOT being hovered over by the mouse...
		 */
	    } else {

		/*
		 * Draws the regular planet. Fills it with Orange. Also, offsets
		 * it by its radius in order to make its Center as the visual XY
		 * coordinate of the planet. In other words, the XY coordinates
		 * of the planet will be treated as the center of the planet,
		 * not its top left corner.
		 */

		if (Game.Player.getCurrentPlanet().equals(currPlanet)) {

		    g2d.setColor(playerPlanetColor);// Draws the planet with

		    // currPlanet.setDrawSize(3);

		    g2d.fillOval(
			    (int) (currPlanet.getVisualLocation().getX() - currPlanet
				    .getDrawSize() / 2.0), (int) (currPlanet
				    .getVisualLocation().getY() - currPlanet
				    .getDrawSize() / 2.0), currPlanet
				    .getDrawSize(), currPlanet.getDrawSize());

		} else {
		    g2d.setColor(regPlanetColor);// Draws the planet with

		    // currPlanet.setDrawSize(DEFAULTPLANETSIZE);

		    g2d.drawOval(
			    (int) (currPlanet.getVisualLocation().getX() - currPlanet
				    .getDrawSize() / 2.0), (int) (currPlanet
				    .getVisualLocation().getY() - currPlanet
				    .getDrawSize() / 2.0), currPlanet
				    .getDrawSize(), currPlanet.getDrawSize());
		}

	    }

	    /*
	     * If the current planet is currently being hovered over by the
	     * mouse...
	     */
	    if (currPlanet.isClickable()) {

		/*
		 * Calculates where the info Box should be displayed.
		 */
		rectWidth = 120;// Width of the info Box.
		rectHeight = 80;// Height of the info Box.

		/*
		 * Sets the XY coordinates of the info Box.
		 */
		rectX = (int) currPlanet.getVisualLocation().getX() - rectWidth;
		rectY = (int) currPlanet.getVisualLocation().getY();

		/*
		 * Makes sure that the infoBox won't go outside of the screen.
		 */
		if (rectX <= 0) {
		    rectX = 0;
		}

		if (rectY + rectHeight >= this.getHeight()) {
		    rectY = this.getHeight() - rectHeight;
		}

	    }

	}

	// DRAW THE TRAVEL DISTANCE RADIUS

	g2d.setColor(radiusColor);// radius color

	// currPlanet.setDrawSize(3);

	final int distRadiusSize = 2 * (int) (((Game.Player.getShip()
		.getCurrFuelLevel() * space.getFuelDistanceRatio())
		/ (space.getGridSize().getWidth()) * this.getWidth()));
	final Planet currPlayerPlanet = Game.Player.getCurrentPlanet();

	g2d.drawOval(
		(int) (currPlayerPlanet.getVisualLocation().getX() - distRadiusSize / 2.0),
		(int) (Game.Player.getCurrentPlanet().getVisualLocation()
			.getY() - distRadiusSize / 2.0), distRadiusSize,
		distRadiusSize);

	/*
	 * This is where the infoBox is being drawn, since we want it to be
	 * drawn on top of the map, after everything else has been drawn.
	 */
	g2d.setColor(boxColor);// Sets the color of the infoBox.
	g2d.fillRect(rectX, rectY, rectWidth, rectHeight);// Draws a filled
							  // rectangle in the
							  // right spot.
	g2d.setColor(infoBoxNameColor);// This is the color of the planet Names.
	final int sideDisplacement = 5; // How much room is left from the sides,
					// for
					// visual purposes.

	/*
	 * This prints out the text on top of the Info Box.
	 */
	if (selectedPlanet != null) {// Makes sure that there is a selected
				     // planet.
	    final Font font = new Font(Font.SANS_SERIF, Font.BOLD, TEXTSIZE);// Sets
									     // the
									     // font
									     // of
									     // the
									     // text.
	    g.setFont(font);

	    /*
	     * Writes the name of the planet.
	     */
	    g2d.drawString(selectedPlanet.getName(), rectX + sideDisplacement,
		    rectY + TEXTSIZE + sideDisplacement);

	    final Font font2 = new Font(Font.SANS_SERIF, Font.BOLD, 12);// Sets
									// the
									// font
									// of
									// the
									// smaller
									// text.
	    g.setFont(font2);
	    g2d.setColor(infoBoxTextColor);

	    /*
	     * Writes out the Development and the Government of the planet.
	     */
	    g2d.drawString(selectedPlanet.getDevelopment(), rectX
		    + sideDisplacement, rectY + 2 * TEXTSIZE + sideDisplacement); // $codepro.audit.disable
										  // expressionValue
	    g2d.drawString(selectedPlanet.getGovernment(), rectX
		    + sideDisplacement, rectY + 3 * TEXTSIZE + sideDisplacement); // $codepro.audit.disable
										  // expressionValue

	    /*
	     * Writes distance to the planet.
	     */

	    final int distLength = space.calcDistance(
		    Game.Player.getCurrentPlanet(), selectedPlanet);

	    if (distLength > Game.Player.getShip().getCurrFuelLevel()) {
		g2d.setColor(Color.red);
	    } else {
		g2d.setColor(infoBoxTextColor);
	    }
	    g2d.drawString("Fuel Cost: " + distLength,
		    rectX + sideDisplacement, rectY + 4 * TEXTSIZE // $codepro.audit.disable
								   // expressionValue
			    + sideDisplacement);
	}

    }

    /**
     * This handles the mouse movement event.
     * 
     * @param e
     *            The Mouse Event that represents mouse movement.
     */

    @Override
    public void mouseMoved(MouseEvent e) {

	final Planet[] planetArray = space.getPlanets();// Stores an array of
							// the
							// planets.
	/*
	 * Checks if there is already a planet that is Clickable. Used when
	 * there are two planets very close to each other, in which case the
	 * first planet that was triggered as Clickable also triggers
	 * foundClosestPlanet to True, so that any following planets will not be
	 * Clickable.
	 */
	boolean foundClosestPlanet = false;

	/*
	 * Runs through all the planets, calculates their distances from the
	 * mouse, and sets their Draw Sizes.
	 */
	for (int i = 0; i < planetArray.length; i++) {

	    /*
	     * Calculates the distance from the mouse. Note: Uses
	     * VisualLocations of the planets.
	     */
	    double distanceFromMouse = Math.sqrt(Math.pow(e.getX()
		    - planetArray[i].getVisualLocation().getX(), 2)
		    + Math.pow(e.getY()
			    - planetArray[i].getVisualLocation().getY(), 2));

	    /*
	     * Sets teh Draw Size of the planet based on its distance from the
	     * mouse.
	     */
	    planetArray[i].setDrawSize(((int) (MAXPLANETSIZE + (MAXPLANETSIZE)
		    * (MOUSERANGE - distanceFromMouse) / MOUSERANGE)));
	    if (planetArray[i].getDrawSize() < DEFAULTPLANETSIZE) {
		planetArray[i].setDrawSize(DEFAULTPLANETSIZE);// Makes the
							      // planets have
							      // a min size.
	    }
	    if (planetArray[i].equals(Game.Player.getCurrentPlanet())
		    && planetArray[i].getDrawSize() < PLAYERPLANETSIZE) {
		planetArray[i].setDrawSize(PLAYERPLANETSIZE);
	    }

	    /*
	     * If the mouse is within ActivationDistance from a planet (close
	     * enough to click it), then check if there is another Clickable
	     * planet already found. if not, then make this planet Clickable.
	     * This allows this planet to then be drawn in a different color,
	     * and the infoBox to be placed by the planet.
	     */
	    if (Math.abs(distanceFromMouse) <= ACTIVATIONDISTANCE) {
		if (!foundClosestPlanet) {
		    planetArray[i].setClickable(true);
		    foundClosestPlanet = true;
		} else {
		    planetArray[i].setClickable(false);
		}
	    } else {
		planetArray[i].setClickable(false);
	    }
	}

	repaint(); // Redraws all of the planets and components,
		   // to reflect the changes that takes place as a result of
		   // mouse movement.

    }

    /**
     * Required by MouseMotionListener, but currently unused.
     * 
     * @param e
     *            The mouse dragged event.
     */
    @Override
    public void mouseDragged(MouseEvent e) {
	System.out.println("Dragged!");
    }

    /**
     * Required by the MouseListener. Checks if the mouse is clicked. If it is
     * clicked while the mouse is hovering by a planet, whose state changes to
     * Clickable, then does stuff that deals with that planet. Currently, it
     * just prints out information.
     * 
     * @param e
     *            A Mouse Event
     */
    @Override
    public void mouseClicked(MouseEvent e) {
	for (int i = 0; i < space.getPlanets().length; i++) {
	    if (space.getPlanets()[i].isClickable()) {
		/*
		 * System.out.println("You Clicked on planet "+space.getPlanets()
		 * [i].getName()+"," +
		 * " which is at the "+space.getPlanets()[i].
		 * getDevelopment()+" state of development, and which has a "+
		 * space.getPlanets()[i].getGovernment()+" government.");
		 */
		// Game.Player.setCurrentPlanet(space.getPlanets()[i]);
		boolean flag = false;
		if (space.canTravel(space.getPlanets()[i])
			&& !space.getPlanets()[i].equals(Game.Player
				.getCurrentPlanet())) {
		    flag = true;
		}

		space.travel(space.getPlanets()[i]);

		if (flag) {
		    // Game.Player.getCurrentPlanet().setDrawSize(DEFAULTPLANETSIZE);
		    space.getPlanets()[i].setDrawSize(2 * MAXPLANETSIZE); // $codepro.audit.disable
									  // expressionValue
		}
		Game.GameScreen.repaint();
		repaint();
	    }
	}
    }

    /**
     * Required by MouseListener, but unused here.
     * 
     * @param e
     *            The associated event that triggers this.
     */
    @Override
    public void mousePressed(MouseEvent e) {
	System.out.print("");
    }

    /**
     * Required by MouseListener, but unused here.
     * 
     * @param e
     *            The associated event that triggers this.
     */
    @Override
    public void mouseReleased(MouseEvent e) {
	System.out.print("");
    }

    /**
     * Required by MouseListener, but unused here.
     * 
     * @param e
     *            The associated event that triggers this.
     */
    @Override
    public void mouseEntered(MouseEvent e) {
	System.out.print("");
    }

    /**
     * Required by MouseListener, but unused here.
     * 
     * @param e
     *            The associated event that triggers this.
     */
    @Override
    public void mouseExited(MouseEvent e) {
	System.out.print("");
    }

}
